#include "GameCommon.h"

#include <QThread>
#include <QMessageBox>



QtMessageHandler MessageHandlerInfo::m_fnQtMessageHandler = nullptr;
MessageHandlerInfo *MessageHandlerInfo::s_pMessageHandlerInfo = nullptr;
FileWriteTask *MessageHandlerInfo::m_pFileWriteTask = nullptr;

MessageHandlerInfo::MessageHandlerInfo()
{
}

MessageHandlerInfo::~MessageHandlerInfo()
{
    if(m_fnQtMessageHandler != nullptr)
    {
        m_fnQtMessageHandler = nullptr;
        qInstallMessageHandler(nullptr);
    }

    m_pFileWriteTask->destroy();
    m_pFileWriteTask->thread()->wait();
}


MessageHandlerInfo* MessageHandlerInfo::create()
{
    if(s_pMessageHandlerInfo != nullptr)
        return s_pMessageHandlerInfo;

    s_pMessageHandlerInfo = new MessageHandlerInfo;
    s_pMessageHandlerInfo->m_pFileWriteTask = new FileWriteTask;
    return s_pMessageHandlerInfo;
}

void MessageHandlerInfo::MessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    //static QMutex mutex;
    QString filename;
    QString text;
    QDateTime current_datetime = QDateTime::currentDateTime();

    switch(type)
    {
    case QtInfoMsg:
        if(s_pMessageHandlerInfo->m_strInfoFile.isEmpty())
            break;
        filename = s_pMessageHandlerInfo->m_strInfoFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat));
        text = QString("Info:");
        break;

    case QtDebugMsg:
        if(s_pMessageHandlerInfo->m_strDebugFile.isEmpty())
            break;
        filename = s_pMessageHandlerInfo->m_strDebugFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat));
        text = QString("Debug:");
        break;

    case QtWarningMsg:
        if(s_pMessageHandlerInfo->m_strWarningFile.isEmpty())
            break;
        filename = s_pMessageHandlerInfo->m_strWarningFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat));
        text = QString("Warning:");
        break;

    case QtCriticalMsg:
        if(s_pMessageHandlerInfo->m_strCriticalFile.isEmpty())
            break;
        filename = s_pMessageHandlerInfo->m_strCriticalFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat));
        text = QString("Critical:");
        break;

    case QtFatalMsg:
        if(s_pMessageHandlerInfo->m_strFatalFile.isEmpty())
            break;
        filename = s_pMessageHandlerInfo->m_strFatalFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat));
        text = QString("Fatal:");
        break;

    default:
        if(s_pMessageHandlerInfo->m_strOthersFile.isEmpty())
            break;
        filename = s_pMessageHandlerInfo->m_strOthersFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat));
        text = QString("Unkown:");
        break;
    }


    if(!filename.isEmpty())
    {
        QString context_info = QString("File:(%1) Line:(%2)").arg(QString(context.file)).arg(context.line);
        text = QString("[%1] %2 %3 %4\r\n")
                .arg(current_datetime.toString("yyyy-MM-dd hh:mm:ss.zzz ddd"))
                .arg(text)
                .arg(context_info)
                .arg(msg)
                ;

        QMetaObject::invokeMethod(s_pMessageHandlerInfo->m_pFileWriteTask,"WriteTextToFile"
                                  ,Qt::AutoConnection
                                  //,Q_RETURN_ARG(QVariant, v)
                                  ,Q_ARG(QIODevice::OpenMode, QIODevice::WriteOnly | QIODevice::Append)
                                  ,Q_ARG(const QString, filename)
                                  ,Q_ARG(const QString, text)
                                  );
    }

    //Debug会记录所有类型
    if(type != QtDebugMsg && !s_pMessageHandlerInfo->m_strDebugFile.isEmpty())
    {
        QMetaObject::invokeMethod(s_pMessageHandlerInfo->m_pFileWriteTask,"WriteTextToFile"
                                  ,Qt::AutoConnection
                                  //,Q_RETURN_ARG(QVariant, v)
                                  ,Q_ARG(QIODevice::OpenMode, QIODevice::WriteOnly | QIODevice::Append)
                                  ,Q_ARG(const QString, s_pMessageHandlerInfo->m_strDebugFile.arg(current_datetime.toString(s_pMessageHandlerInfo->m_strDatetimeFormat)))
                                  ,Q_ARG(const QString, text)
                                  );
    }


    if(m_fnQtMessageHandler != nullptr)
        m_fnQtMessageHandler(type, context, msg);
}





#if defined(Q_OS_WIN32)   //win

//异常捕获函数
LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException){
    //创建 Dump 文件
    QDateTime current_datetime = QDateTime::currentDateTime();
    QString current_date = current_datetime.toString("yyyy_MM_dd_hh_mm_ss");
    //dmp文件的命名
    QString dumpText = "Dump_"+current_date+".dmp";
    EXCEPTION_RECORD *record = pException->ExceptionRecord;
    QString errCode(QString::number(record->ExceptionCode, 16));
    QString errAddr(QString::number((uint)record->ExceptionAddress, 16));
    QString errFlag(QString::number(record->ExceptionFlags, 16));
    QString errPara(QString::number(record->NumberParameters, 16));
    HANDLE hDumpFile = CreateFile((LPCWSTR)dumpText.utf16(),
             GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hDumpFile != INVALID_HANDLE_VALUE) {
        MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
        dumpInfo.ExceptionPointers = pException;
        dumpInfo.ThreadId = GetCurrentThreadId();
        dumpInfo.ClientPointers = TRUE; //FALSE;
        //将dump信息写入dmp文件
        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),hDumpFile
                          , MiniDumpNormal  //MiniDumpWithFullMemory
                          , &dumpInfo, NULL, NULL);
        CloseHandle(hDumpFile);
    }
    else{
        qCritical() << "[!ERROR]hDumpFile == null";
    }

    //创建消息提示
    QString msg = QString("ErrorCode：%1\nErrorAddr：%2\nErrorFlag：%3\nErrorPara：%4")
                   .arg(errCode)
                   .arg(errAddr)
                   .arg(errFlag)
                   .arg(errPara)
                   ;
    qCritical() << msg;
    QMessageBox::critical(nullptr, "服务器Crash", msg);

    return EXCEPTION_EXECUTE_HANDLER;//已经处理了异常,可以结束了
    //return EXCEPTION_CONTINUE_EXECUTION;//从异常处继续执行
}

#endif
